#include "PoolAllocator.h"

#include <iostream>


PoolAllocator::PoolAllocator(void)
	:
	m_size(DEFAULT_ALLOC_SIZE),
	m_pBaseAddress(new char[DEFAULT_ALLOC_SIZE]),
	m_pFirstAlloc(0),
	m_allocated(0)
{

}
PoolAllocator::~PoolAllocator(void)
{
	free(m_pBaseAddress);
}

void* PoolAllocator::Allocate(size_t size)
{
	if ( (m_size - m_allocated) < (size + sizeof(AllocInfo)) ) // Not enough free space!!!
	{
		return 0;
	}

	AllocInfo* pInfo = 0;
	if (!m_pFirstAlloc) // if is empty
	{
		pInfo = (AllocInfo*)m_pBaseAddress;
		pInfo->next = 0;
		m_pFirstAlloc = pInfo;
	}
	else
	{
		if ( (size + sizeof(AllocInfo)) <= (reinterpret_cast<char*>(m_pFirstAlloc) - m_pBaseAddress) ) // if alloc size fits at the beggining of the block
		{
			AllocInfo* pTempFirst = m_pFirstAlloc;
			
			pInfo = (AllocInfo*)m_pBaseAddress;
			pInfo->next = pTempFirst;
			m_pFirstAlloc = pInfo;
		}
		else
		{
			bool bIsBetween = false;
			AllocInfo* pPrev = 0;
			AllocInfo* pNext = m_pFirstAlloc;
			while (pNext->next != 0) 
			{
				pPrev = pNext;
				pNext = pNext->next;

				if ((size + sizeof(AllocInfo)) <= (reinterpret_cast<char*>(pNext) - (reinterpret_cast<char*>(pPrev) + sizeof(AllocInfo) + pPrev->size))) // if alloc size fits in between two blocks
				{
					pNext = pPrev;
					bIsBetween = true;
					break;
				}	// if not found any free space in between block, then we place it at the end
			}
			pInfo = reinterpret_cast<AllocInfo*>(reinterpret_cast<char*>(pNext) + pNext->size + sizeof(AllocInfo));


			if (reinterpret_cast<char*>(pInfo) + sizeof(AllocInfo) + size > m_pBaseAddress + m_size) // Does the block fit at the end?
			{
				return 0;
			}

			if (bIsBetween)
			{
				pInfo->next = pNext->next;
			}
			else
			{
				pInfo->next = 0;
			}
			pNext->next = pInfo;
		}
	}
	pInfo->size = size;

	m_allocated += size + sizeof(AllocInfo);

	return (void*)(pInfo + 1);
}
void PoolAllocator::Free(void* ptr)
{
	AllocInfo* pInfo = reinterpret_cast<AllocInfo*>(ptr) - 1;

	AllocInfo* pPrev = 0;
	AllocInfo* pNext = m_pFirstAlloc;

	bool bFound = false;

	while (pNext)
	{
		if (pNext == pInfo)
		{
			if (pPrev)
			{
				if (pInfo->next)
				{
					pPrev->next = pInfo->next;
				}
				else
				{
					pPrev->next = 0;
				}
			}
			else
			{
				if (pInfo->next)
				{
					m_pFirstAlloc = pInfo->next;
				}
				else
				{
					m_pFirstAlloc = 0;
				}
			}
			bFound = true;
			break;
		}
		pPrev = pNext;
		pNext = pNext->next;
	}
	if (bFound)
	{
		m_allocated -= pInfo->size + sizeof(AllocInfo);
	}
}
void PoolAllocator::FreeAll(void)
{
	m_pFirstAlloc = 0;
	m_allocated = 0;
}

size_t PoolAllocator::GetAllocatedSpace(void) const
{
	return m_allocated;
}
size_t PoolAllocator::GetTotalSize(void) const
{
	return m_size;
}